from miniopy_async import Minio, S3Error
import aiofiles
import aiohttp
from typing import Optional
from pathlib import Path
from aiopathlib import AsyncPath

from common.log import log
from common.event import get_event_loop
from common.exception.errors import CustomException

from config.base import get_settings

settings = get_settings()

class MinioUtil():

    def __init__(
        self,
        client: Minio = None
    ):
        
        self._client = client if client else settings.minio.get_client()
        
    async def upload_async(
        self, 
        file_path: str | Path, 
        object_name: str, 
        bucket_name: str,
        content_type="application/octet-stream"
    ) -> bool:
        """上传文件到 Minio

        Args:
            file_path (str): 文件路径
            object_name (str): 上传对象名
            bucket_name (str): 存储位置

        Returns:
            bool: 上传成功 true，否则 false
        """
        try:
            file = AsyncPath(file_path)
            file_stat = await file.stat()
            async with aiofiles.open(file_path, mode="rb") as fs:
                await self._client.put_object(
                    bucket_name=bucket_name,
                    object_name=object_name,
                    data=fs,
                    length=file_stat.st_size,
                    content_type=content_type,
                    part_size=-1
                )
            log.info(f"{file_path} 成功上传到 {bucket_name}/{object_name}")
            
            return True
        except Exception as e:
            log.error(f"minio 上传文件 {object_name} 失败: {e}")
        return False
    
    async def download_async(
        self, 
        save_path: str | Path, 
        object_name: str, 
        bucket_name: str
    ) -> bool:
        """从 Minio 下载文件到指定位置

        Args:
            save_path (str): 文件保存位置
            object_name (str): 对象名
            bucket_name (str): 存储位置
        """
        errors = None
        try:
            async with aiohttp.ClientSession() as session:
                response = await self._client.get_object(
                    bucket_name=bucket_name,
                    object_name=object_name,
                    session=session
                )
                async with aiofiles.open(save_path, 'wb') as f:
                    async for chunk in response.content.iter_chunked(1024 * 1024):
                        await f.write(chunk)
                        
            return True
        except S3Error as e:
            errors = f"MinIO S3 错误: {e}"
        except Exception as e:
            errors = f"minio 下载文件 {object_name} 失败: {e}"
        log.error(errors)
        raise CustomException(errors, 500)
    
    def upload_sync(
        self, 
        file_path: str, 
        object_name: str, 
        bucket_name: str,
        content_type="application/octet-stream"
    ) -> bool:
        loop = get_event_loop()
        return loop.run_until_complete(
            self.upload_async(
                file_path=file_path,
                object_name=object_name,
                bucket_name=bucket_name,
                content_type=content_type
            )
        )
        
    def download_sync(
        self, 
        save_path: str, 
        object_name: str, 
        bucket_name: str
    ) -> bool:
        loop = get_event_loop()
        return loop.run_until_complete(
            self.download_async(
                save_path=save_path,
                object_name=object_name,
                bucket_name=bucket_name
            )
        )